/*
 * CAudioDecoder.cpp
 *
 *  Created on: 2016年4月14日
 *      Author: terry
 */

#include "CAudioDecoder.h"
#include "FfmpegUtil.h"


namespace av
{


CAudioDecoder::CAudioDecoder(MediaDecoderSink* sink):
		m_sink(sink),
		m_codecCtx()
{
}

CAudioDecoder::~CAudioDecoder()
{
	close();
}

int CAudioDecoder::open(const MediaFormat& fmt)
{
	AVCodec* pCodec = avcodec_find_decoder((AVCodecID)fmt.m_audioCodec);
	if (!pCodec)
	{
		return EBADF;
	}

	m_codecCtx = avcodec_alloc_context3(pCodec);

	m_codecCtx->refcounted_frames = 1;

	m_codecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
	m_codecCtx->channels = fmt.m_channels;
	m_codecCtx->sample_rate = fmt.m_sampleRate;

	if (fmt.m_audioConfig.size())
	{
		m_codecCtx->extradata = (uint8_t*)av_memdup(fmt.m_audioConfig.c_str(), fmt.m_audioConfig.size());
		m_codecCtx->extradata_size = fmt.m_audioConfig.size();
	}

	if (avcodec_open2(m_codecCtx, pCodec, NULL) < 0)
	{
		avcodec_close(m_codecCtx);
		av_free(m_codecCtx);
		m_codecCtx = NULL;
		return EBADF;
	}

	return 0;
}

void CAudioDecoder::close()
{
	if (m_codecCtx)
	{
		avcodec_close(m_codecCtx);
		av_free(m_codecCtx);
		m_codecCtx = NULL;
	}


}

bool CAudioDecoder::isOpen()
{
	return (m_codecCtx != NULL);
}

int CAudioDecoder::input(AVPacketPtr pkt)
{
	if (!pkt)
	{
		return EINVAL;
	}

	int ret = -1;

    AVPacket pktIn;
    av_init_packet(&pktIn);
    av_packet_copy_props(&pktIn, pkt.get());
    pktIn.size = pkt->size;
    pktIn.data = pkt->data;
    
	while (pktIn.size > 0)
	{
		AVFramePtr frame(av_frame_alloc(), ffmpeg::AVFrameDeleter());

		int gotFrame = 0;
		int bytes = avcodec_decode_audio4(m_codecCtx, frame.get(), &gotFrame, &pktIn);
		if (bytes <= 0)
		{
			break;
		}

		ret = 0;

		pktIn.size -= bytes;
		pktIn.data += bytes;

		if (gotFrame)
		{
            if (frame->channel_layout <= 0)
            {
                frame->channel_layout = av_get_default_channel_layout(frame->channels);
            }

			fireFrame(frame);
		}
	}

	return ret;
}

void CAudioDecoder::setSink(MediaDecoderSink* sink)
{
	m_sink = sink;
}

void CAudioDecoder::flush()
{
	//
}

bool CAudioDecoder::getOutFormat(RawMediaFormat& fmt)
{
	if (!m_codecCtx)
	{
		return false;
	}

	fmt.m_channels = m_codecCtx->channels;
	fmt.m_sampleRate = m_codecCtx->sample_rate;
	fmt.m_sampleFormat = m_codecCtx->sample_fmt;

	return true;
}

void CAudioDecoder::fireFrame(AVFramePtr frame)
{
	if (m_sink)
	{
		m_sink->onDecodeFrame(frame);
	}
}







} /* namespace av */
